local japi = require 'jass.japi'
local jass = require 'jass.common'
local dbg  = require 'jass.debug'

local handle_manager = handle_manager_class:create()
local message = ac.message
local sync = {}
setmetatable(sync, sync)
message.sync = sync

sync.perfixs = {}
sync.alltriggers = {}
sync.allacts = setmetatable({}, {__mode = 'kv'})
sync.perfixs = {}

--- 注册同步回调
function sync:register(name)
    local trigger = sync.alltriggers[name]
    if not trigger then
        local index = handle_manager:allocate()
        if index > 100 then 
            log.error('超过注册上限')
            return 
        end 
        sync.perfixs[index] = name 
        sync.perfixs[name] = index 
        trigger = ac.trigger.create(name)
        trigger.remove = function(t)
            for i = #trigger.actors, 1, -1 do 
                local act = trigger.actors[i]
                act:remove()
                sync.allacts[act] = nil
            end 
            sync.alltriggers[name] = nil
        end
        sync.alltriggers[name] = trigger
    end 
    return function (func)
        local act = trigger:add_actor(func)
        act.key = "同步事件2>" .. name
        sync.allacts[act] = true
        return act
    end
end 

sync.on     = sync.register
sync.event  = sync.register

---全局事件回调
---@param name string 事件名称
---@param ... any 参数
function sync:notify(name, ...)
    local trigger = sync.alltriggers[name]
    if not trigger then return end
    for i = #trigger.actors, 1, -1 do 
        trigger.actors[i]:execute(...)
    end
end

--- 发起同步

--- 用本地点目标命令发起同步
--- 本地玩家必须能够获取到当前的选择单位才可以使用
--- 通常用于处理单位消息 （商店售卖,无目标技能顺发,无目标物品顺发,等等）
--- 0xD0037 命令ID 这个命令不可以用于其他地方使用 切记切记
--- 最多可以填两个参数,且必须是1000以内的数字
--- 切莫高频率发起同步,掉线

--- 单个逻辑帧只能同步1次
local clock = os.clock()

function sync:__call(name, ...)

    local c = os.clock()

    if (c-clock) < 0.01 then 
        -- print("本次同步被屏蔽")
        return 
    end 

    clock = c 

    --- 判断没有选择单位则直接跳出
    local handle = japi.GetRealSelectUnit()
    if handle == nil or handle == 0 then 
        return 
    end

    --- 没有注册过的同步 跳出
    if not sync.perfixs[name] then 
        return 
    end 

    --- 获取序号
    local index = sync.perfixs[name]
    if not index then 
        return 
    end

    local params = {...} 
    local syncer = {
        [1] = 0.001 + (jass.GetPlayerId(jass.GetLocalPlayer()) + 1) / 100,
        [2] = 0.001 + index / 100,
    }

    for i = 1,2 do 
        local value = params[i] or 0
        if type(value) ~= 'number' then 
            log.error('只能同步数字')
            return
        end
        syncer[i] = (syncer[i] + math.abs(math.floor(value))) * ((value >= 0) and 1 or -1)
    end 

    message:order_call{
        "order_point",
        0xD0037, 
        0, 
        syncer[1], 
        syncer[2], 
        params[3] or (1 << 5),
    }
end 

--- 初始化
local function init()
    
    local function action()
        local order = jass.GetIssuedOrderId()
        if order ~= 0xD0037 then 
            return 
        end

        local x = jass.GetOrderPointX()
        local y = jass.GetOrderPointY()

        local player
        local sync_name

        local params = {
            [1] = 0,
            [2] = 0,
        }

        do 
            local symbol = (x >= 0) and 1 or -1
            local leftV, rightV = math.modf( math.abs(x) )
            rightV = math.floor(rightV * 100)
    
            --- 检查第一个参数是不是有效的玩家ID 
            if rightV < 1 or rightV > 16 then 
                return
            end
    
            params[1] = leftV * symbol
            player = ac.player and ac.player(rightV) or jass.Player(rightV-1)
        end

        do
            local symbol = (y >= 0) and 1 or -1
            local leftV, rightV = math.modf( math.abs(y) )
            rightV = math.floor(rightV * 100)
    
            params[2] = leftV * symbol
            sync_name = sync.perfixs[rightV]
            --- 检查第二个参数是否是有效的事件ID 
            if not sync_name then 
                return
            end
        end

        --- 能够获得单位对象则获取单位对象
        local unit = ac.unit and ac.unit(jass.GetTriggerUnit()) or jass.GetTriggerUnit() 
        -- print( "message同步" ,sync_name, player, unit, params[1], params[2])
        sync:notify(sync_name, player, unit, params[1], params[2])
    end 

    local handle = jass.CreateTrigger()
    dbg.handle_ref(handle)
    jass.TriggerAddAction(handle,action)
    for i = 1,16 do
        jass.TriggerRegisterPlayerUnitEvent(handle, jass.Player(i-1), jass.EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, nil)
    end
end 

init()

return sync 